#include <ProcessThread/ProcessThread.h>
#include <Memory/Heap.h>
#include <Memory/Paging.h>
#include <Utility/CLib.h>


void InitializeMainThread(void)
{
    LPThreadControlBlock lpCurrentThread = GetCurrentThread(); /* 当前线程 b */
    /* b 线程由 a 线程在创建进程 B 时创建 */
    /* 进程 B 目前只有空壳 PCB 及其主线程 b，主线程 b 此时只有内核栈 */
    LPProcessControlBlock lpCurrentProcess = lpCurrentThread->lpProcess; /* 由线程 b 找到所属进程 B */
    KERNEL_ASSERT(lpCurrentProcess, "Current Thread 0x%x does not have its parent.\r\n", lpCurrentThread);
    /* 将可执行文件解析到用户空间 */
    lpCurrentThread->lpStartAddress = LoadProgram(lpCurrentProcess->lpszExecPath);
    /* B 目前没有用户堆，为其建立内存堆，在建堆前用户态的地址空间除了加载程序任何函数不可使用，否则程序无法正确加载 */
    lpCurrentProcess->lpHeap = AllocatePageForUser(1, PTE_WRITEABLE | PTE_USER); /* 为用户创建堆描述符表 */
    memset(lpCurrentProcess->lpHeap, 0, PAGE_SIZE); /* 堆描述符表清空 */
    /* 初始化主线程运行环境 */
    /* 创建用户栈 */
    lpCurrentThread->dwUserStackSize = ((lpCurrentThread->dwUserStackSize -  1) / 4096 + 1) * 4096; /* 栈大小按页面对齐 */
    LPVOID lpBottomOfStack = AllocateStackForUser(lpCurrentThread->dwUserStackSize / 4096); /* 在用户空间创建栈帧 */
    lpCurrentThread->lpUserStack = (LPVOID)((DWORD)lpBottomOfStack + lpCurrentThread->dwUserStackSize); /* 栈顶地址 */
    LPDWORD lpUserStack = lpCurrentThread->lpUserStack; /* 用户栈 */
    /* 用户栈中栈顶保存 main 函数需要的三个参数 argc argv envp */
    // lpUserStack -= 3; /* 预留三个参数 */
    *(lpUserStack--) = 0;
    *(lpUserStack--) = 0;
    *(lpUserStack--) = 0;
    /* 参数设置完成后，使用 iret 返回到用户态执行线程 */
    lpCurrentThread->context.dwCS = SELECTOR_USER_CODE;
    lpCurrentThread->context.dwDS = SELECTOR_USER_DATA;
    lpCurrentThread->context.dwSS = SELECTOR_USER_DATA;
    lpCurrentThread->context.dwEFLAGS = 0x3200; /* IOPL = 3, IF = 1 */
    lpCurrentThread->context.dwESP = (DWORD)lpUserStack;
    lpCurrentThread->context.dwEBP = (DWORD)lpUserStack;
    lpCurrentThread->context.dwEIP = (DWORD)lpCurrentThread->lpStartAddress; /* _start 程序入口地址 */
    IntExceptContext context;
    memcpy(&context, &lpCurrentThread->context, sizeof(IntExceptContext)); /* 在栈帧中伪造中断现场 */
    RestoreThreadContext((DWORD)&context);
}